Passed
Branch v8.x (6e4ba7)
by Rafael S.
02:10
created

wav-buffer-reader.js ➔ bitDepthFromFmt_   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 11
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 9
c 1
b 0
f 0
nc 4
nop 1
dl 0
loc 11
rs 9.3333
1
/*
2
 * Copyright (c) 2018 Rafael da Silva Rocha.
3
 *
4
 * Permission is hereby granted, free of charge, to any person obtaining
5
 * a copy of this software and associated documentation files (the
6
 * "Software"), to deal in the Software without restriction, including
7
 * without limitation the rights to use, copy, modify, merge, publish,
8
 * distribute, sublicense, and/or sell copies of the Software, and to
9
 * permit persons to whom the Software is furnished to do so, subject to
10
 * the following conditions:
11
 *
12
 * The above copyright notice and this permission notice shall be
13
 * included in all copies or substantial portions of the Software.
14
 *
15
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
 *
23
 */
24
25
/**
26
 * @fileoverview Get structured wav data out of buffers.
27
 * @see https://github.com/rochars/wavefile
28
 */
29
30
import {riffChunks, findChunk_} from '../vendor/riff-chunks.js';
31
import BufferIO from './bufferio.js';
32
33
let io = new BufferIO();
34
35
/**
36
 * Set up the WaveFile object from a byte buffer.
37
 * @param {!Uint8Array} buffer The buffer.
38
 * @param {boolean} samples True if the samples should be loaded.
39
 * @param {!WavBuffer} wav A WavBuffer instance.
40
 * @throws {Error} If container is not RIFF, RIFX or RF64.
41
 * @throws {Error} If no 'fmt ' chunk is found.
42
 * @throws {Error} If no 'data' chunk is found.
43
 */
44
export default function readWavBuffer(buffer, samples, wav) {
45
  io.head_ = 0;
46
  let uInt32_ = {bits: 32, be: false};
47
  let uInt16_ = {bits: 16, be: false};
48
  readRIFFChunk_(buffer, wav, uInt32_, uInt16_);
49
  /** @type {!Object} */
50
  let chunk = riffChunks(buffer);
51
  readDs64Chunk_(buffer, chunk.subChunks, wav, uInt32_);
52
  readFmtChunk_(buffer, chunk.subChunks, wav, uInt32_, uInt16_);
53
  readFactChunk_(buffer, chunk.subChunks, wav, uInt32_);
54
  readBextChunk_(buffer, chunk.subChunks, wav, uInt32_, uInt16_);
55
  readCueChunk_(buffer, chunk.subChunks, wav, uInt32_);
56
  readSmplChunk_(buffer, chunk.subChunks, wav, uInt32_);
57
  readDataChunk_(buffer, chunk.subChunks, samples, wav);
58
  readJunkChunk_(buffer, chunk.subChunks, wav);
59
  readLISTChunk_(buffer, chunk.subChunks, wav, uInt32_, uInt16_);
60
}
61
62
/**
63
 * Read the RIFF chunk a wave file.
64
 * @param {!Uint8Array} bytes A wav buffer.
65
 * @param {!WavBuffer} wav A WavBuffer instance.
66
 * @throws {Error} If no 'RIFF' chunk is found.
67
 * @private
68
 */
69
function readRIFFChunk_(bytes, wav, uInt32_, uInt16_) {
70
  io.head_ = 0;
71
  wav.container = io.readString_(bytes, 4);
72
  if (['RIFF', 'RIFX', 'RF64'].indexOf(wav.container) === -1) {
73
    throw Error('Not a supported format.');
74
  }
75
  uInt16_.be = wav.container === 'RIFX';
76
  uInt32_.be = uInt16_.be;
77
  wav.chunkSize = io.read_(bytes, uInt32_);
78
  wav.format = io.readString_(bytes, 4);
79
  if (wav.format != 'WAVE') {
80
    throw Error('Could not find the "WAVE" format identifier');
81
  }
82
}
83
84
/**
85
 * Read the 'fmt ' chunk of a wave file.
86
 * @param {!Uint8Array} buffer The wav file buffer.
87
 * @param {!Object} signature The file signature.
88
 * @param {!WavBuffer} wav A WavBuffer instance.
89
 * @throws {Error} If no 'fmt ' chunk is found.
90
 * @private
91
 */
92
function readFmtChunk_(buffer, signature, wav, uInt32_, uInt16_) {
93
  /** @type {?Object} */
94
  let chunk = findChunk_(signature, 'fmt ');
95
  if (chunk) {
96
    io.head_ = chunk.chunkData.start;
97
    wav.fmt.chunkId = chunk.chunkId;
98
    wav.fmt.chunkSize = chunk.chunkSize;
99
    wav.fmt.audioFormat = io.read_(buffer, uInt16_);
100
    wav.fmt.numChannels = io.read_(buffer, uInt16_);
101
    wav.fmt.sampleRate = io.read_(buffer, uInt32_);
102
    wav.fmt.byteRate = io.read_(buffer, uInt32_);
103
    wav.fmt.blockAlign = io.read_(buffer, uInt16_);
104
    wav.fmt.bitsPerSample = io.read_(buffer, uInt16_);
105
    readFmtExtension_(buffer, wav, uInt32_, uInt16_);
106
  } else {
107
    throw Error('Could not find the "fmt " chunk');
108
  }
109
}
110
111
/**
112
 * Read the 'fmt ' chunk extension.
113
 * @param {!Uint8Array} buffer The wav file buffer.
114
 * @param {!WavBuffer} wav A WavBuffer instance.
115
 * @private
116
 */
117
function readFmtExtension_(buffer, wav, uInt32_, uInt16_) {
118
  if (wav.fmt.chunkSize > 16) {
119
    wav.fmt.cbSize = io.read_(buffer, uInt16_);
120
    if (wav.fmt.chunkSize > 18) {
121
      wav.fmt.validBitsPerSample = io.read_(buffer, uInt16_);
122
      if (wav.fmt.chunkSize > 20) {
123
        wav.fmt.dwChannelMask = io.read_(buffer, uInt32_);
124
        wav.fmt.subformat = [
125
          io.read_(buffer, uInt32_),
126
          io.read_(buffer, uInt32_),
127
          io.read_(buffer, uInt32_),
128
          io.read_(buffer, uInt32_)];
129
      }
130
    }
131
  }
132
}
133
134
/**
135
 * Read the 'fact' chunk of a wav file.
136
 * @param {!Uint8Array} buffer The wav file buffer.
137
 * @param {!Object} signature The file signature.
138
 * @param {!WavBuffer} wav A WavBuffer instance.
139
 * @private
140
 */
141
function readFactChunk_(buffer, signature, wav, uInt32_) {
142
  /** @type {?Object} */
143
  let chunk = findChunk_(signature, 'fact');
144
  if (chunk) {
145
    io.head_ = chunk.chunkData.start;
146
    wav.fact.chunkId = chunk.chunkId;
147
    wav.fact.chunkSize = chunk.chunkSize;
148
    wav.fact.dwSampleLength = io.read_(buffer, uInt32_);
149
  }
150
}
151
152
/**
153
 * Read the 'cue ' chunk of a wave file.
154
 * @param {!Uint8Array} buffer The wav file buffer.
155
 * @param {!Object} signature The file signature.
156
 * @param {!WavBuffer} wav A WavBuffer instance.
157
 * @private
158
 */
159
function readCueChunk_(buffer, signature, wav, uInt32_) {
160
  /** @type {?Object} */
161
  let chunk = findChunk_(signature, 'cue ');
162
  if (chunk) {
163
    io.head_ = chunk.chunkData.start;
164
    wav.cue.chunkId = chunk.chunkId;
165
    wav.cue.chunkSize = chunk.chunkSize;
166
    wav.cue.dwCuePoints = io.read_(buffer, uInt32_);
167
    for (let i=0; i<wav.cue.dwCuePoints; i++) {
168
      wav.cue.points.push({
169
        dwName: io.read_(buffer, uInt32_),
170
        dwPosition: io.read_(buffer, uInt32_),
171
        fccChunk: io.readString_(buffer, 4),
172
        dwChunkStart: io.read_(buffer, uInt32_),
173
        dwBlockStart: io.read_(buffer, uInt32_),
174
        dwSampleOffset: io.read_(buffer, uInt32_),
175
      });
176
    }
177
  }
178
}
179
180
/**
181
 * Read the 'smpl' chunk of a wave file.
182
 * @param {!Uint8Array} buffer The wav file buffer.
183
 * @param {!Object} signature The file signature.
184
 * @param {!WavBuffer} wav A WavBuffer instance.
185
 * @private
186
 */
187
function readSmplChunk_(buffer, signature, wav, uInt32_) {
188
  /** @type {?Object} */
189
  let chunk = findChunk_(signature, 'smpl');
190
  if (chunk) {
191
    io.head_ = chunk.chunkData.start;
192
    wav.smpl.chunkId = chunk.chunkId;
193
    wav.smpl.chunkSize = chunk.chunkSize;
194
    wav.smpl.dwManufacturer = io.read_(buffer, uInt32_);
195
    wav.smpl.dwProduct = io.read_(buffer, uInt32_);
196
    wav.smpl.dwSamplePeriod = io.read_(buffer, uInt32_);
197
    wav.smpl.dwMIDIUnityNote = io.read_(buffer, uInt32_);
198
    wav.smpl.dwMIDIPitchFraction = io.read_(buffer, uInt32_);
199
    wav.smpl.dwSMPTEFormat = io.read_(buffer, uInt32_);
200
    wav.smpl.dwSMPTEOffset = io.read_(buffer, uInt32_);
201
    wav.smpl.dwNumSampleLoops = io.read_(buffer, uInt32_);
202
    wav.smpl.dwSamplerData = io.read_(buffer, uInt32_);
203
    for (let i=0; i<wav.smpl.dwNumSampleLoops; i++) {
204
      wav.smpl.loops.push({
205
        dwName: io.read_(buffer, uInt32_),
206
        dwType: io.read_(buffer, uInt32_),
207
        dwStart: io.read_(buffer, uInt32_),
208
        dwEnd: io.read_(buffer, uInt32_),
209
        dwFraction: io.read_(buffer, uInt32_),
210
        dwPlayCount: io.read_(buffer, uInt32_),
211
      });
212
    }
213
  }
214
}
215
216
/**
217
 * Read the 'data' chunk of a wave file.
218
 * @param {!Uint8Array} buffer The wav file buffer.
219
 * @param {!Object} signature The file signature.
220
 * @param {boolean} samples True if the samples should be loaded.
221
 * @param {!WavBuffer} wav A WavBuffer instance.
222
 * @throws {Error} If no 'data' chunk is found.
223
 * @private
224
 */
225
function readDataChunk_(buffer, signature, samples, wav) {
226
  /** @type {?Object} */
227
  let chunk = findChunk_(signature, 'data');
228
  if (chunk) {
229
    wav.data.chunkId = 'data';
230
    wav.data.chunkSize = chunk.chunkSize;
231
    if (samples) {
232
      wav.data.samples = buffer.slice(
233
        chunk.chunkData.start,
234
        chunk.chunkData.end);
235
    }
236
  } else {
237
    throw Error('Could not find the "data" chunk');
238
  }
239
}
240
241
/**
242
 * Read the 'bext' chunk of a wav file.
243
 * @param {!Uint8Array} buffer The wav file buffer.
244
 * @param {!Object} signature The file signature.
245
 * @param {!WavBuffer} wav A WavBuffer instance.
246
 * @private
247
 */
248
function readBextChunk_(buffer, signature, wav, uInt32_, uInt16_) {
249
  /** @type {?Object} */
250
  let chunk = findChunk_(signature, 'bext');
251
  if (chunk) {
252
    io.head_ = chunk.chunkData.start;
253
    wav.bext.chunkId = chunk.chunkId;
254
    wav.bext.chunkSize = chunk.chunkSize;
255
    wav.bext.description = io.readString_(buffer, 256);
256
    wav.bext.originator = io.readString_(buffer, 32);
257
    wav.bext.originatorReference = io.readString_(buffer, 32);
258
    wav.bext.originationDate = io.readString_(buffer, 10);
259
    wav.bext.originationTime = io.readString_(buffer, 8);
260
    wav.bext.timeReference = [
261
      io.read_(buffer, uInt32_),
262
      io.read_(buffer, uInt32_)];
263
    wav.bext.version = io.read_(buffer, uInt16_);
264
    wav.bext.UMID = io.readString_(buffer, 64);
265
    wav.bext.loudnessValue = io.read_(buffer, uInt16_);
266
    wav.bext.loudnessRange = io.read_(buffer, uInt16_);
267
    wav.bext.maxTruePeakLevel = io.read_(buffer, uInt16_);
268
    wav.bext.maxMomentaryLoudness = io.read_(buffer, uInt16_);
269
    wav.bext.maxShortTermLoudness = io.read_(buffer, uInt16_);
270
    wav.bext.reserved = io.readString_(buffer, 180);
271
    wav.bext.codingHistory = io.readString_(
272
      buffer, wav.bext.chunkSize - 602);
273
  }
274
}
275
276
/**
277
 * Read the 'ds64' chunk of a wave file.
278
 * @param {!Uint8Array} buffer The wav file buffer.
279
 * @param {!Object} signature The file signature.
280
 * @param {!WavBuffer} wav A WavBuffer instance.
281
 * @throws {Error} If no 'ds64' chunk is found and the file is RF64.
282
 * @private
283
 */
284
function readDs64Chunk_(buffer, signature, wav, uInt32_) {
285
  /** @type {?Object} */
286
  let chunk = findChunk_(signature, 'ds64');
287
  if (chunk) {
288
    io.head_ = chunk.chunkData.start;
289
    wav.ds64.chunkId = chunk.chunkId;
290
    wav.ds64.chunkSize = chunk.chunkSize;
291
    wav.ds64.riffSizeHigh = io.read_(buffer, uInt32_);
292
    wav.ds64.riffSizeLow = io.read_(buffer, uInt32_);
293
    wav.ds64.dataSizeHigh = io.read_(buffer, uInt32_);
294
    wav.ds64.dataSizeLow = io.read_(buffer, uInt32_);
295
    wav.ds64.originationTime = io.read_(buffer, uInt32_);
296
    wav.ds64.sampleCountHigh = io.read_(buffer, uInt32_);
297
    wav.ds64.sampleCountLow = io.read_(buffer, uInt32_);
298
    //if (wav.ds64.chunkSize > 28) {
299
    //  wav.ds64.tableLength = unpack(
300
    //    chunkData.slice(28, 32), uInt32_);
301
    //  wav.ds64.table = chunkData.slice(
302
    //     32, 32 + wav.ds64.tableLength);
303
    //}
304
  } else {
305
    if (wav.container == 'RF64') {
306
      throw Error('Could not find the "ds64" chunk');
307
    }
308
  }
309
}
310
311
/**
312
 * Read the 'LIST' chunks of a wave file.
313
 * @param {!Uint8Array} buffer The wav file buffer.
314
 * @param {!Object} signature The file signature.
315
 * @param {!WavBuffer} wav A WavBuffer instance.
316
 * @private
317
 */
318
function readLISTChunk_(buffer, signature, wav, uInt32_, uInt16_) {
319
  /** @type {?Object} */
320
  let listChunks = findChunk_(signature, 'LIST', true);
321
  if (listChunks === null) {
322
    return;
323
  }
324
  for (let j=0; j < listChunks.length; j++) {
325
    /** @type {!Object} */
326
    let subChunk = listChunks[j];
327
    wav.LIST.push({
328
      chunkId: subChunk.chunkId,
329
      chunkSize: subChunk.chunkSize,
330
      format: subChunk.format,
331
      subChunks: []});
332
    for (let x=0; x<subChunk.subChunks.length; x++) {
333
      readLISTSubChunks_(subChunk.subChunks[x],
334
        subChunk.format, buffer, wav, uInt32_, uInt16_);
335
    }
336
  }
337
}
338
339
/**
340
 * Read the sub chunks of a 'LIST' chunk.
341
 * @param {!Object} subChunk The 'LIST' subchunks.
342
 * @param {string} format The 'LIST' format, 'adtl' or 'INFO'.
343
 * @param {!Uint8Array} buffer The wav file buffer.
344
 * @param {!WavBuffer} wav A WavBuffer instance.
345
 * @private
346
 */
347
function readLISTSubChunks_(subChunk, format, buffer, wav, uInt32_, uInt16_) {
348
  if (format == 'adtl') {
349
    if (['labl', 'note','ltxt'].indexOf(subChunk.chunkId) > -1) {
350
      io.head_ = subChunk.chunkData.start;
351
      /** @type {!Object<string, string|number>} */
352
      let item = {
353
        chunkId: subChunk.chunkId,
354
        chunkSize: subChunk.chunkSize,
355
        dwName: io.read_(buffer, uInt32_)
356
      };
357
      if (subChunk.chunkId == 'ltxt') {
358
        item.dwSampleLength = io.read_(buffer, uInt32_);
359
        item.dwPurposeID = io.read_(buffer, uInt32_);
360
        item.dwCountry = io.read_(buffer, uInt16_);
361
        item.dwLanguage = io.read_(buffer, uInt16_);
362
        item.dwDialect = io.read_(buffer, uInt16_);
363
        item.dwCodePage = io.read_(buffer, uInt16_);
364
      }
365
      item.value = io.readZSTR_(buffer, io.head_);
366
      wav.LIST[wav.LIST.length - 1].subChunks.push(item);
367
    }
368
  // RIFF INFO tags like ICRD, ISFT, ICMT
369
  } else if(format == 'INFO') {
370
    io.head_ = subChunk.chunkData.start;
371
    wav.LIST[wav.LIST.length - 1].subChunks.push({
372
      chunkId: subChunk.chunkId,
373
      chunkSize: subChunk.chunkSize,
374
      value: io.readZSTR_(buffer, io.head_)
375
    });
376
  }
377
}
378
379
/**
380
 * Read the 'junk' chunk of a wave file.
381
 * @param {!Uint8Array} buffer The wav file buffer.
382
 * @param {!Object} signature The file signature.
383
 * @param {!WavBuffer} wav A WavBuffer instance.
384
 * @private
385
 */
386
function readJunkChunk_(buffer, signature, wav) {
387
  /** @type {?Object} */
388
  let chunk = findChunk_(signature, 'junk');
389
  if (chunk) {
390
    wav.junk = {
391
      chunkId: chunk.chunkId,
392
      chunkSize: chunk.chunkSize,
393
      chunkData: [].slice.call(buffer.slice(
394
        chunk.chunkData.start,
395
        chunk.chunkData.end))
396
    };
397
  }
398
}
399